/**
 * @author Pedro J Toro
 * CSC8050 Design and Analysis of Algorithms 
 */
public class Line {
	private Point p; // 1st point in the line
	private Point q; // 2nd point in the line
	private double slope; // slope of the line
	private double yIntercept; // y intercept of the line
	private boolean noSlope; // does it have a slope?
	private boolean zeroSlope; // does it have zero slope?

	/**
	 * Default constructor.
	 * @param p, 1st point in the line
	 * @param q, 2nd point in the line
	 */
	public Line(Point p, Point q) {
		super();
		this.p = p;
		this.q = q;

		updateSlope();
	}

	/**
	 * Gets the 1st point in the line.
	 * @return the p
	 */
	public Point getP() {
		return p;
	}

	/**
	 * Sets the 1st point in the line.
	 * @param p, the p to set
	 */
	public void setP(Point p) {
		this.p = p;

		// update slope information
		updateSlope();
	}

	/**
	 * Gets the 2nd point in the line.
	 * @return the q
	 */
	public Point getQ() {
		return q;
	}

	/**
	 * Sets the 2nd point in the line.
	 * @param q, the q to set
	 */
	public void setQ(Point q) {
		this.q = q;

		// update slope information
		updateSlope();
	}

	/**
	 * Gets the slope of the line.
	 * @return the slope
	 */
	public double getSlope() {
		return slope;
	}

	/**
	 * Gets the y-intercept of the line.
	 * @return the yIntercept
	 */
	public double getYIntercept() {
		return yIntercept;
	}

	/**
	 * Returns true or false if the line has no slope.
	 * @return the noSlope
	 */
	public boolean isNoSlope() {
		return noSlope;
	}

	/**
	 * Returns true or false if the line has zero slope.
	 * @return the zeroSlope
	 */
	public boolean isZeroSlope() {
		return zeroSlope;
	}

	/**
	 * Update the slope information and y-intercept values of the line.
	 */
	private void updateSlope() {
		if (p.getX() == q.getX()) {
			// line has no slope
			noSlope = true;
		} else if (p.getY() == q.getY()) {
			// line has a zero slope
			zeroSlope = true;
			slope = 0;
			yIntercept = p.getY();
		} else {
			// line has a slope not zero
			slope = (p.getY() - q.getY()) / (p.getX() - q.getX());
			yIntercept = p.getY() - slope * p.getX();
			noSlope = false;
			zeroSlope = false;
		}
	}

	/**
	 * Checks if the given point is over the line.
	 * @param checkPoint, point to check against
	 * @return 1 if under, 0 if over, -1 if on the line
	 */
	public int isOver(Point checkPoint) {
		double newY; // temporary y value

		double tempX = checkPoint.getX(); // x value of point to check against
		double tempY = checkPoint.getY(); // y value of point to check against

		if (noSlope) {
			// line has no slope
			if (tempX > p.getX()) {
				// point is to the right of the line
				return 1;
			} else if (tempX == p.getX()) {
				// point is on the line, check if it passes the line
				checkLimitsY(tempY);
				return -1;
			}
		} else {
			// calculate new y value using current slope and tempX
			newY = (slope * tempX) + yIntercept;

			if (tempY > newY) {
				// point if over the line
				return 1;
			} else if (newY == tempY) {
				// point is on the line, check if it passes the line
				checkLimitsX(tempX);
				return -1;
			}			
		}
		
		// point is over the line
		return 0;
	}

	/**
	 * Checks if the point is under the line.
	 * @param checkPoint, point to check against
	 * @return 1 if under, 0 if over, -1 if on the line
	 */
	public int isUnder(Point checkPoint) {
		double newY; // temporary y value

		double tempX = checkPoint.getX(); // x value of point to check against
		double tempY = checkPoint.getY(); // y value of point to check against

		if (noSlope) {
			// line has no slope
			if (tempX < p.getX()) {
				// point is to the left of the line
				return 1;
			} else if (tempX == p.getX()) {
				// point is on the line, check if it passes the line
				checkLimitsY(tempY);
				return -1;
			}
		} else {
			// calculate new y value using current slope and tempX
			newY = (slope * tempX) + yIntercept;

			if (tempY < newY) {
				// point if under the line
				return 1;
			} else if (newY == tempY) {
				// point is on the line, check if it passes the line
				checkLimitsX(tempX);
				return -1;
			}			
		}

		// point is over the line
		return 0;
	}

	/**
	 * Checks if x value is inside of the line. If it is outside, then adjust
	 * x value of the line accordingly to make it be in the line.
	 * @param tempX, x value to check against in the line
	 */
	private void checkLimitsX(double tempX) {
		if (p.getX() > q.getX()) {
			// P is greater and Q is lesser
			if (tempX > p.getX()) {
				// make x value in P equal to new x value
				p.setX(tempX);
			} else if (tempX < q.getX()) {
				// make x value in Q equal to new x value 
				q.setX(tempX);
			}
		} else {
			// Q is greater and P is lesser
			if (tempX > q.getX()) {
				// make x value in Q equal to new x value
				q.setX(tempX);
			} else if (tempX < p.getX()) {
				// make x value in P equal to new x value
				p.setX(tempX);
			}
		}
	}

	/**
	 * Checks if y value is inside of the line. If it is outside, then adjust
	 * y value of the line accordingly to make it be in the line.
	 * @param tempY, y value to check against in the line
	 */
	private void checkLimitsY(double tempY) {
		if (p.getY() > q.getY()) {
			// P is greater and Q is lesser
			if (tempY > p.getY()) {
				// make y value in P equal to new y value
				p.setY(tempY);
			} else if (tempY < q.getY()) {
				// make y value in Q equal to new y value
				q.setY(tempY);
			}
		} else {
			// Q is greater and P is lesser
			if (tempY > q.getY()) {
				// make y value in Q equal to new y value
				q.setY(tempY);
			} else if (tempY < p.getY()) {
				// make y value in P equal to new y value
				p.setY(tempY);
			}
		}
	}

	/* (non-Javadoc)
	 * @see java.lang.Object#equals(java.lang.Object)
	 */
	@Override
	/**
	 * Override for equals method. Returns true if both Lines have the same points,
	 * false otherwise.
	 */
	public boolean equals(Object arg0) {
		if (arg0 instanceof Line) {
			Line thatLine = (Line) arg0;

			// check if points in the lines equal
			return (this.p.equals(thatLine.p) && this.q.equals(thatLine.q)) || (this.p.equals(thatLine.q) && this.q.equals(thatLine.p)); 
		}

		return false;
	}	
	
	/**
	 * Checks if point is an end-point of the line. 
	 * @param point, point to check against
	 * @return true is point is an end-point of the line, false otherwise
	 */
	public boolean hasPoint(Point point) {
		return this.p.equals(point) || this.q.equals(point);
	}	

}